//! Zenop2p 工具函数：地址解析与端口绑定
use libp2p::multiaddr::{Multiaddr, Protocol};
use std::net::{IpAddr, SocketAddr};
use tokio::net::TcpListener;
use tokio::time::{sleep, Duration};

/// 从 Multiaddr 提取 TCP 地址（IP:端口）
pub fn extract_tcp_addr(addr: &Multiaddr) -> Option<SocketAddr> {
    let mut ip = None;
    let mut port = None;
    for proto in addr.iter() {
        match proto {
            Protocol::Ip4(a) => ip = Some(IpAddr::V4(a)),
            Protocol::Ip6(a) => ip = Some(IpAddr::V6(a)),
            Protocol::Tcp(p) => port = Some(p),
            _ => continue,
        }
    }
    match (ip, port) {
        (Some(ip), Some(port)) => Some(SocketAddr::new(ip, port)),
        _ => None,
    }
}

/// 带重试的端口绑定（避免冲突）
pub async fn bind_with_retry(base_port: u16, max_retries: usize) -> Result<SocketAddr, String> {
    let mut retries = 0;
    loop {
        let port = base_port + retries as u16;
        let addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
        match TcpListener::bind(addr).await {
            Ok(_) => return Ok(addr),
            Err(e) => {
                retries += 1;
                if retries >= max_retries {
                    return Err(format!("端口 {} 绑定失败（重试{}次）: {}", base_port, max_retries, e));
                }
                sleep(Duration::from_secs(1)).await;
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use libp2p::multiaddr::Multiaddr;
    use std::net::{Ipv4Addr, Ipv6Addr};

    #[test]
    fn test_extract_tcp_addr_ipv4() {
        // 测试 IPv4 地址提取
        let addr: Multiaddr = "/ip4/192.168.1.1/tcp/8080".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        let socket_addr = result.unwrap();
        assert_eq!(socket_addr.ip(), IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
        assert_eq!(socket_addr.port(), 8080);
    }

    #[test]
    fn test_extract_tcp_addr_ipv6() {
        // 测试 IPv6 地址提取
        let addr: Multiaddr = "/ip6/2001:db8::1/tcp/9090".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        let socket_addr = result.unwrap();
        assert_eq!(socket_addr.ip(), IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)));
        assert_eq!(socket_addr.port(), 9090);
    }

    #[test]
    fn test_extract_tcp_addr_with_extra_protocols() {
        // 测试包含额外协议的地址提取
        let addr: Multiaddr = "/ip4/10.0.0.1/tcp/3000/p2p/12D3KooWTest".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        let socket_addr = result.unwrap();
        assert_eq!(socket_addr.ip(), IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)));
        assert_eq!(socket_addr.port(), 3000);
    }

    #[test]
    fn test_extract_tcp_addr_no_ip() {
        // 测试没有IP地址的情况
        let addr: Multiaddr = "/tcp/8080".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_none());
    }

    #[test]
    fn test_extract_tcp_addr_no_port() {
        // 测试没有端口的情况
        let addr: Multiaddr = "/ip4/192.168.1.1".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_none());
    }

    #[test]
    fn test_extract_tcp_addr_empty() {
        // 测试空地址
        let addr = Multiaddr::empty();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_none());
    }

    #[test]
    fn test_extract_tcp_addr_udp_protocol() {
        // 测试UDP协议（应该被忽略）
        let addr: Multiaddr = "/ip4/192.168.1.1/udp/8080".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_none());
    }

    #[tokio::test]
    async fn test_bind_with_retry_success() {
        // 测试端口绑定成功
        let result = bind_with_retry(30000, 3).await;
        assert!(result.is_ok());
        let socket_addr = result.unwrap();
        assert_eq!(socket_addr.ip(), "0.0.0.0".parse::<IpAddr>().unwrap());
        assert!(socket_addr.port() >= 30000 && socket_addr.port() <= 30002);
    }

    #[tokio::test]
    async fn test_bind_with_retry_port_in_use() {
        // 测试端口被占用时的重试逻辑
        // 首先绑定一个端口
        let listener = TcpListener::bind("0.0.0.0:30010").await.unwrap();
        
        // 然后尝试绑定相同端口（应该重试到下一个端口）
        let result = bind_with_retry(30010, 3).await;
        assert!(result.is_ok());
        let socket_addr = result.unwrap();
        assert_eq!(socket_addr.port(), 30011); // 应该重试到下一个端口
        
        drop(listener); // 释放监听器
    }

    #[tokio::test]
    async fn test_bind_with_retry_max_retries_exceeded() {
        // 测试超过最大重试次数的情况
        // 绑定多个端口来模拟所有端口都被占用
        let mut listeners = Vec::new();
        for i in 0..5 {
            let port = 30020 + i;
            let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await.unwrap();
            listeners.push(listener);
        }
        
        // 尝试绑定，但最大重试次数为3，应该失败
        let result = bind_with_retry(30020, 3).await;
        assert!(result.is_err());
        let error_msg = result.unwrap_err();
        assert!(error_msg.contains("端口 30020 绑定失败（重试3次）"));
        
        // 清理监听器
        drop(listeners);
    }

    #[test]
    fn test_extract_tcp_addr_edge_cases() {
        // 测试边界情况
        
        // 测试最小端口
        let addr: Multiaddr = "/ip4/127.0.0.1/tcp/1".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        assert_eq!(result.unwrap().port(), 1);
        
        // 测试最大端口
        let addr: Multiaddr = "/ip4/127.0.0.1/tcp/65535".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        assert_eq!(result.unwrap().port(), 65535);
        
        // 测试回环地址
        let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        assert_eq!(result.unwrap().ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
        
        // 测试广播地址
        let addr: Multiaddr = "/ip4/255.255.255.255/tcp/8080".parse().unwrap();
        let result = extract_tcp_addr(&addr);
        assert!(result.is_some());
        assert_eq!(result.unwrap().ip(), IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)));
    }
}